package com.zxmr.hospital.info.action;

import java.util.ArrayList;
import java.util.List;

import com.alibaba.fastjson.JSON;
import com.zxmr.action.Request;
import com.zxmr.action.Response;
import com.zxmr.action.ResponsePayload;
import com.zxmr.action.ResultCode;
import com.zxmr.domain.account.AuthorizationByRole;
import com.zxmr.domain.account.Role;
import com.zxmr.domain.account.User;
import com.zxmr.domain.component.Action;
import com.zxmr.domain.component.ActionGroup;
import com.zxmr.domain.service.AuthorizationService;
import com.zxmr.domain.service.ComponentService;
import com.zxmr.hospital.info.action.dto.RoleAuthList;
import com.zxmr.navigate.ExecuteException;
import com.zxmr.navigate.Executor;
import com.zxmr.util.Utils;

public class RoleAuthorize extends Executor{

	private enum Method{
		query,
		update;
	}
	class RoleAuthorizeUpdate{
		int action;
		int role;
		boolean enable;
		
		public int getAction() {
			return action;
		}
		public void setAction(int action) {
			this.action = action;
		}
		public int getRole() {
			return role;
		}
		public void setRole(int role) {
			this.role = role;
		}
		public boolean isEnable() {
			return enable;
		}
		public void setEnable(boolean enable) {
			this.enable = enable;
		}
	}
	@Override
	public void execute(Request request, Response response) {
		
		
		Method method = Enum.valueOf(Method.class, this.getElement().getMethod());
		try
		{
			switch(method)
			{
			case query:
				query(request,response);
				break;
			case update:
				update(request,response);
				break;
			}
		}catch(ExecuteException e)
		{
			ResponsePayload payload = new ResponsePayload();
			payload.setValue(e.getMessage());
			response.setPayload(payload);
			response.setResult(ResultCode.COMPONENT_ERROR);
		}
	}
	private void query(Request request, Response response)throws ExecuteException
	{
		User user = (User)request.getAccount();

		int roleID = parseRole(request);
		int parent = parseParent(request);
			
		// all sub actions under one group
		List<Action> actions = getAllActionForGroup(user.getCompany(),parent);
		List<AuthorizationByRole> authorizes = getAllAuthorizedAction(roleID,user.getCompany(),parent);
		List<ActionGroup> groups = queryGroup(parent,user.getCompany());
			
		// all group under one group
		List<RoleAuthList> dto = constructActionDTO(actions,authorizes);
		List<RoleAuthList> groupDto = constructGroupDTO(groups);
		
		dto.addAll(groupDto);
			
		reply(response,dto);

	}
	private void reply(Response response,List<RoleAuthList> dto)
	{
		ResponsePayload payload = new ResponsePayload();
		payload.setValue(JSON.toJSONString(dto));
		response.setPayload(payload);
	}
	private List<RoleAuthList> constructGroupDTO(List<ActionGroup> groups)
	{
		List<RoleAuthList> list = new ArrayList<RoleAuthList>();
		if(groups == null || groups.isEmpty())return list;
		
		for(ActionGroup action : groups)
		{
			RoleAuthList ral = new RoleAuthList();
			ral.setComponent(action.getComponent());
			ral.setText(action.getDisplay());
			ral.setId(String.valueOf(action.getId()));
			ral.setState("closed");
			ral.setGroup(true);
			list.add(ral);
		}
		
		return list;
	}
	private List<RoleAuthList> constructActionDTO(List<Action> actions,List<AuthorizationByRole> authorizes)
	{
		List<RoleAuthList> list = new ArrayList<RoleAuthList>();
		if(actions == null || actions.isEmpty())return list;
		
		for(Action action : actions)
		{
			RoleAuthList ral = new RoleAuthList();
			ral.setComponent(action.getComponent());
			ral.setText(action.getDisplay());
			ral.setId(String.valueOf(action.getId()));
			ral.setState("open");
			ral.setGroup(false);
			
			// check if the action is authorized to Role
			for(AuthorizationByRole auth : authorizes)
			{
				if(auth.getAction() == action.getId())
				{
					ral.setChecked(true);
				}
			}
			list.add(ral);
		}
		
		return list;
	}
	private List<AuthorizationByRole> getAllAuthorizedAction(int role,int company,int group)
	{
		AuthorizationService as = new AuthorizationService();
		return as.queryAllAuthorizationWithGroup(role, company, group);
	}
	private List<Action> getAllActionForGroup(int company,int parent)
	{
		AuthorizationService as = new AuthorizationService();
		return as.queryAllActionForGroup(company, parent);
	}
	private List<ActionGroup> queryGroup(int parent,int company)
	{
		ComponentService service = new ComponentService();
		return service.queryGroup(company, parent);
	}
	private void validateRole(User user, int roleID) throws ExecuteException {
		
		AuthorizationService as = new AuthorizationService();
		Role r = as.queryRole(roleID);
		if(r == null || r.getCompany() != user.getCompany())
		{
			throw new ExecuteException("you are not authorized to the action");
		}
	}
	private int parseRole(Request request) throws ExecuteException {
		String role = request.getRequestParameters().get("role");
		int roleID;
		try
		{
			roleID = Integer.parseInt(role);
		}catch(Exception e)
		{
			throw new ExecuteException("role id should be integer");
		}
		return roleID;
	}
	private int parseParent(Request request) throws ExecuteException {
		String parent = request.getRequestParameters().get("id");
		if(Utils.isStringEmptyWithTrim(parent))return 0;
		try
		{
			return Integer.parseInt(parent);
		}catch(Exception e)
		{
			throw new ExecuteException("parent id should be integer");
		}
	}
	
	private void update(Request request, Response response)throws ExecuteException
	{
		try
		{
			RoleAuthorizeUpdate rau = parseUpdateRequest(request);
			updateAuthorization(rau,request.getAccount());

		}catch(Exception e)
		{
			if(e instanceof ExecuteException)
			{
				throw e;
			}else
			{
				e.printStackTrace();
				throw new ExecuteException("internal error when processing update");
			}
		}
		
	}
	private void updateAuthorization(RoleAuthorizeUpdate rau,User user)throws ExecuteException
	{
		AuthorizationService service = new AuthorizationService();
		boolean result = true;
		
		if(rau.isEnable())
		{
			AuthorizationByRole abr = service.queryAuthorization(rau.getRole(), rau.getAction());
			if(abr == null)result = service.addAuthorization(rau.getRole(),rau.getAction());
		}else
		{
			result = service.deleteAuthorization(rau.getRole(), rau.getAction());
		}
		
		if(!result)throw new ExecuteException("internal error when update authorization");
	}
	private RoleAuthorizeUpdate parseUpdateRequest(Request request)throws ExecuteException
	{
		int role = Integer.parseInt(request.getRequestParameters().get("role"));
		int action = Integer.parseInt(request.getRequestParameters().get("action"));
		boolean enabled = Boolean.parseBoolean(request.getRequestParameters().get("enable"));
		User user = (User)request.getAccount();
		
		validateRole(user,role);
		validateAction(user,action);
		
		RoleAuthorizeUpdate abr = new RoleAuthorizeUpdate();
		abr.setRole(role);
		abr.setAction(action);
		abr.setEnable(enabled);
		
		return abr;
	}
	private void validateAction(User user,int action)throws ExecuteException
	{
		AuthorizationService service = new AuthorizationService();
		// check if the action is the for the company
		if(!service.validateActionByCompany(user.getCompany(), action))
		{
			throw new ExecuteException("you are not authorized to update action for the role");
		}
	}
}
